package org.xx.armory.commons;

import java.util.Date;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 参数验证工具类。
 *
 * @author Haart
 */
public final class Validators {
    /**
     * 禁止构造{@link Validators}实例。
     */
    private Validators() {
    }

    /**
     * 检测参数值是否是{@code null}, 如果是则抛出{@link IllegalArgumentException}。
     *
     * @param <T>
     *         参数{@code v}的类型。
     * @param v
     *         参数值。
     * @param argName
     *         参数名。
     * @return 传入的参数值。
     * @throws IllegalArgumentException
     *         如果参数{@code v}是{@code null}。
     * @see Objects#requireNonNull(Object, String)
     */
    public static <T> T notNull(
            final T v,
            final String argName
    ) {
        if (v == null) {
            if (argName != null) {
                throw new IllegalArgumentException(
                        String.format("argument \"%s\" cannot be null", argName.trim()));
            } else {
                throw new IllegalArgumentException("argument cannot be null");
            }
        } else {
            return v;
        }
    }

    /**
     * 检测参数值是否是{@code null}或者只包含空白字符, 如果是则抛出{@link IllegalArgumentException}。
     *
     * @param v
     *         参数值。
     * @param argName
     *         参数名。
     * @return 传入的参数值，已去掉首尾空白字符。
     * @throws IllegalArgumentException
     *         如果参数{@code v}是{@code null}或者只包含空白字符。
     */
    public static String notBlank(
            String v,
            final String argName
    ) {
        if (v == null) {
            if (argName != null) {
                throw new IllegalArgumentException(
                        String.format("argument \"%s\" cannot be null", argName.trim()));
            } else {
                throw new IllegalArgumentException("argument cannot be null");
            }
        } else {
            v = v.trim();
            if (v.isEmpty()) {
                if (argName != null) {
                    throw new IllegalArgumentException(
                            String.format("argument \"%s\" cannot be blank", argName.trim()));
                } else {
                    throw new IllegalArgumentException("argument cannot be blank");
                }
            } else {
                return v.trim();
            }
        }
    }

    /**
     * 检测参数值是否是{@code null}或者不包含任何元素, 如果是则抛出{@link IllegalArgumentException}。
     *
     * @param <E>
     *         数组元素的类型。
     * @param v
     *         参数值。
     * @param argName
     *         参数名。
     * @return 传入的参数值。
     * @throws IllegalArgumentException
     *         如果参数{@code array}是{@code null}或者不包含任何元素。
     */
    public static <E> E[] notEmpty(
            E[] v,
            String argName
    ) {
        if (v == null) {
            if (argName != null) {
                throw new IllegalArgumentException(
                        String.format("argument \"%s\" cannot be null", argName.trim()));
            } else {
                throw new IllegalArgumentException("argument cannot be null");
            }
        } else if (v.length == 0) {
            if (argName != null) {
                throw new IllegalArgumentException(
                        String.format("argument \"%s\" cannot be empty", argName.trim()));
            } else {
                throw new IllegalArgumentException("argument cannot be empty");
            }
        } else {
            return v;
        }
    }

    /**
     * 检测参数值是否是{@code null}或者不包含任何元素, 如果是则抛出{@link IllegalArgumentException}。
     *
     * @param v
     *         参数值。
     * @param argName
     *         参数名。
     * @return 传入的参数值。
     * @throws NullPointerException
     *         如果参数{@code array}是{@code null}。
     * @throws IllegalArgumentException
     *         如果参数{@code array}不包含任何元素。
     */
    public static char[] notEmpty(
            char[] v,
            String argName
    ) {
        if (v == null) {
            if (argName != null) {
                throw new IllegalArgumentException(
                        String.format("argument \"%s\" cannot be null", argName.trim()));
            } else {
                throw new IllegalArgumentException("argument cannot be null");
            }
        } else if (v.length == 0) {
            if (argName != null) {
                throw new IllegalArgumentException(
                        String.format("argument \"%s\" cannot be empty", argName.trim()));
            } else {
                throw new IllegalArgumentException("argument cannot be empty");
            }
        } else {
            return v;
        }
    }

    /**
     * 检测参数值是否是{@code null}或者不包含任何元素, 如果是则抛出{@link IllegalArgumentException}。
     *
     * @param v
     *         参数值。
     * @param argName
     *         参数名。
     * @return 传入的参数值。
     * @throws NullPointerException
     *         如果参数{@code array}是{@code null}。
     * @throws IllegalArgumentException
     *         如果参数{@code array}不包含任何元素。
     */
    public static byte[] notEmpty(
            byte[] v,
            String argName
    ) {
        if (v == null) {
            if (argName != null) {
                throw new IllegalArgumentException(
                        String.format("argument \"%s\" cannot be null", argName.trim()));
            } else {
                throw new IllegalArgumentException("argument cannot be null");
            }
        } else if (v.length == 0) {
            if (argName != null) {
                throw new IllegalArgumentException(
                        String.format("argument \"%s\" cannot be empty", argName.trim()));
            } else {
                throw new IllegalArgumentException("argument cannot be empty");
            }
        } else {
            return v;
        }
    }

    /**
     * 检测参数值是否是{@code null}或者不包含任何元素, 如果是则抛出{@link IllegalArgumentException}。
     *
     * @param v
     *         参数值。
     * @param argName
     *         参数名。
     * @return 传入的参数值。
     * @throws NullPointerException
     *         如果参数{@code array}是{@code null}。
     * @throws IllegalArgumentException
     *         如果参数{@code array}不包含任何元素。
     */
    public static int[] notEmpty(
            final int[] v,
            final String argName
    ) {
        if (v == null) {
            if (argName != null) {
                throw new IllegalArgumentException(
                        String.format("argument \"%s\" cannot be null", argName.trim()));
            } else {
                throw new IllegalArgumentException("argument cannot be null");
            }
        } else if (v.length == 0) {
            if (argName != null) {
                throw new IllegalArgumentException(
                        String.format("argument \"%s\" cannot be empty", argName.trim()));
            } else {
                throw new IllegalArgumentException("argument cannot be empty");
            }
        } else {
            return v;
        }
    }

    /**
     * 检测参数值是否是{@code null}或者不包含任何元素, 如果是则抛出{@link NullPointerException}或
     * {@link IllegalArgumentException}。
     *
     * @param v
     *         参数值。
     * @param argName
     *         参数名。
     * @return 传入的参数值。
     * @throws NullPointerException
     *         如果参数{@code array}是{@code null}。
     * @throws IllegalArgumentException
     *         如果参数{@code array}不包含任何元素。
     */
    public static long[] notEmpty(
            final long[] v,
            final String argName
    ) {
        if (v == null) {
            if (argName != null) {
                throw new IllegalArgumentException(
                        String.format("argument \"%s\" cannot be null", argName.trim()));
            } else {
                throw new IllegalArgumentException("argument cannot be null");
            }
        } else if (v.length == 0) {
            if (argName != null) {
                throw new IllegalArgumentException(
                        String.format("argument \"%s\" cannot be empty", argName.trim()));
            } else {
                throw new IllegalArgumentException("argument cannot be empty");
            }
        } else {
            return v;
        }
    }

    /**
     * 检测参数值{@code index}是否是合法的数组下标, 如果不是则抛出{@link ArrayIndexOutOfBoundsException}异常。
     *
     * <p>
     * 合法的数组下标应当大于或者等于{@code 0}。
     * </p>
     *
     * @param index
     *         数组下标。
     * @param argName
     *         参数名。
     * @return 传入的参数值。
     * @throws IndexOutOfBoundsException
     *         如果参数{@code index}不是合法的下标。
     */
    public static int validIndex(
            final int index,
            final String argName
    ) {
        if (index < 0) {
            if (argName == null) {
                throw new IndexOutOfBoundsException(
                        String.format("invalid index : %s", index));
            } else {
                throw new IndexOutOfBoundsException(
                        String.format("invalid index value of \"%s\": %s", argName, index));
            }
        } else {
            return index;
        }
    }

    /**
     * 检测参数值{@code index}是否是合法的数组下标, 如果不是则抛出{@link ArrayIndexOutOfBoundsException}异常。
     *
     * <p>
     * 合法的数组下标应当大于或者等于{@code 0}并且小于等于{@code upper}。
     * </p>
     *
     * @param index
     *         数组下标。
     * @param argName
     *         参数名。
     * @param upper
     *         数组下标上界。
     * @return 传入的参数值。
     * @throws IndexOutOfBoundsException
     *         如果参数{@code index}不是合法的下标。
     */
    public static int validIndex(
            final int index,
            final String argName,
            final int upper
    ) {
        if (index < 0 || index > upper) {
            if (argName == null) {
                throw new IndexOutOfBoundsException(
                        String.format("invalid index : %s, upper is %d", index, upper));
            } else {
                throw new IndexOutOfBoundsException(
                        String.format("invalid index value of \"%s\": %s, upper is %d", argName, index, upper));
            }
        } else {
            return index;
        }
    }

    /**
     * 检测参数值是否大于指定的最小值。
     *
     * @param v
     *         参数值。
     * @param argName
     *         参数名。
     * @param min
     *         指定的最小值。
     * @return 传入的参数值。
     * @throws IllegalArgumentException
     *         如果参数{@code v}小于或者等于{@code min}。
     */
    public static int greaterThan(
            final int v,
            final String argName,
            final int min
    ) {
        if (v <= min) {
            if (argName != null) {
                throw new IllegalArgumentException(
                        String.format("invalid value of \"%s\": %d, should > %d", argName, v, min));
            } else {
                throw new IllegalArgumentException(
                        String.format("invalid value: %d, should > %d", v, min));
            }
        } else {
            return v;
        }
    }

    /**
     * 检测参数值是否大于指定的最小值。
     *
     * @param v
     *         参数值。
     * @param argName
     *         参数名。
     * @param min
     *         指定的最小值。
     * @return 传入的参数值。
     * @throws IllegalArgumentException
     *         如果参数{@code v}小于或者等于{@code min}。
     */
    public static long greaterThan(
            final long v,
            final String argName,
            final long min
    ) {
        if (v <= min) {
            if (argName != null) {
                throw new IllegalArgumentException(
                        String.format("invalid value of \"%s\": %d, should > %d", argName, v, min));
            } else {
                throw new IllegalArgumentException(
                        String.format("invalid value: %d, should > %d", v, min));
            }
        } else {
            return v;
        }
    }

    /**
     * 检测参数值是否大于或等于指定的最小值。
     *
     * @param v
     *         参数值。
     * @param argName
     *         参数名。
     * @param min
     *         指定的最小值。
     * @return 传入的参数值。
     * @throws IllegalArgumentException
     *         如果参数{@code v}小于{@code min}。
     */
    public static int greaterThanOrEqual(
            final int v,
            final String argName,
            final int min
    ) {
        if (v < min) {
            if (argName != null) {
                throw new IllegalArgumentException(
                        String.format("invalid value of \"%s\": %d, should >= %d", argName, v, min));
            } else {
                throw new IllegalArgumentException(
                        String.format("invalid value: %d, should >= %d", v, min));
            }
        } else {
            return v;
        }
    }

    /**
     * 检测参数值是否大于或等于指定的最小值。
     *
     * @param v
     *         参数值。
     * @param argName
     *         参数名。
     * @param min
     *         指定的最小值。
     * @return 传入的参数值。
     * @throws IllegalArgumentException
     *         如果参数{@code v}小于{@code min}。
     */
    public static long greaterThanOrEqual(
            final long v,
            final String argName,
            final long min
    ) {
        if (v < min) {
            if (argName != null) {
                throw new IllegalArgumentException(
                        String.format("invalid value of \"%s\": %d, should >= %d", argName, v, min));
            } else {
                throw new IllegalArgumentException(
                        String.format("invalid value: %d, should >= %d", v, min));
            }
        } else {
            return v;
        }
    }

    /**
     * 检测参数值是否大于指定的最小值。
     *
     * @param <T>
     *         待比较的参数值的类型。
     * @param v
     *         参数值。
     * @param argName
     *         参数名。
     * @param min
     *         指定的最小值。
     * @return 传入的参数值。
     * @throws IllegalArgumentException
     *         如果参数{@code v}小于或者等于{@code min}。
     */
    public static <T extends Comparable<T>> T greaterThan(
            final T v,
            final String argName,
            final T min
    ) {
        notNull(v, argName);

        if (v.compareTo(min) <= 0) {
            if (argName != null) {
                throw new IllegalArgumentException(
                        String.format("invalid value of \"%s\": %s, should > %s", argName, v, min));
            } else {
                throw new IllegalArgumentException(
                        String.format("invalid value: %s, should > %s", v, min));
            }
        } else {
            return v;
        }
    }

    /**
     * 检测参数值是否大于或等于指定的最小值。
     *
     * @param <T>
     *         待比较的参数值的类型。
     * @param v
     *         参数值。
     * @param argName
     *         参数名。
     * @param min
     *         指定的最小值。
     * @return 传入的参数值。
     * @throws IllegalArgumentException
     *         如果参数{@code v}小于{@code min}。
     */
    public static <T extends Comparable<T>> T greaterThanOrEqual(
            final T v,
            final String argName,
            final T min
    ) {
        notNull(v, argName);

        if (v.compareTo(min) < 0) {
            if (argName != null) {
                throw new IllegalArgumentException(
                        String.format("invalid value of \"%s\": %s, should > %s", argName,
                                      String.valueOf(v), String.valueOf(min)));
            } else {
                throw new IllegalArgumentException(
                        String.format("invalid value: %s, should > %s", String.valueOf(v),
                                      String.valueOf(min)));
            }
        } else {
            return v;
        }
    }

    /**
     * 检测第一个参数的日期是否在第二个参数的日期之后。
     *
     * @param d1
     *         第一个日期参数值。
     * @param d2
     *         第二个日子参数值。
     * @param argName1
     *         第一个参数的名字。
     * @param argName2
     *         第二个参数的名字。
     * @throws NullPointerException
     *         如果参数{@code d1}是{@code null}或者参数{@code d2}是{@code null}。
     * @throws IllegalArgumentException
     *         如果参数{@code d1}不在参数{@code d2}之后。
     */
    public static void after(
            final Date d1,
            final Date d2,
            final String argName1,
            final String argName2
    ) {
        notNull(d1, argName1);
        notNull(d2, argName2);

        final long ts1 = d1.getTime();
        final long ts2 = d2.getTime();

        if (ts1 <= ts2) {
            throw new IllegalArgumentException(
                    String.format(
                            "invalid value of \"%s\": %s and \"%s\": %s, first should after second",
                            argName1, d1, argName2, d2));
        }
    }

    /**
     * 检测第一个参数的日期是否在第二个参数的日期之后或相等。
     *
     * @param d1
     *         第一个日期参数值。
     * @param d2
     *         第二个日子参数值。
     * @param argName1
     *         第一个参数的名字。
     * @param argName2
     *         第二个参数的名字。
     * @throws NullPointerException
     *         如果参数{@code d1}是{@code null}或者参数{@code d2}是{@code null}。
     * @throws IllegalArgumentException
     *         如果参数{@code d1}不在参数{@code d2}之后并且也不相等。
     */
    public static void afterOrEqual(
            final Date d1,
            final Date d2,
            final String argName1,
            final String argName2
    ) {
        notNull(d1, argName1);
        notNull(d2, argName2);

        final long ts1 = d1.getTime();
        final long ts2 = d2.getTime();

        if (ts1 < ts2) {
            throw new IllegalArgumentException(
                    String.format(
                            "invalid value of \"%s\": %s and \"%s\": %s, first should after or equal second",
                            argName1, d1, argName2, d2));
        }
    }

    /**
     * 判断指定的字符串是否完全符合指定的正则表达式。
     *
     * <p>
     * 判断条件：{@code m.matches() && m.start() == 0 && m.end() == value.length()}
     * </p>
     *
     * @param pattern
     *         指定的正则表达式。
     * @param value
     *         待匹配的字符串，如果是{@code null}则看作{@code ""}。
     * @return 如果匹配则返回{@code true}，否则返回{@code false}。
     * @throws NullPointerException
     *         如果参数pattern是{@code null}。
     * @see java.util.regex.Pattern#matcher(CharSequence)
     * @see java.util.regex.Matcher#start()
     * @see java.util.regex.Matcher#end()
     */
    public static boolean test(
            final Pattern pattern,
            CharSequence value
    ) {
        notNull(pattern, "pattern");
        if (value == null) {
            value = "";
        }

        final Matcher m = pattern.matcher(value);
        return m.matches() && m.start() == 0 && m.end() == value.length();
    }

    /**
     * 检测状态状态是否满足。
     *
     * <p>
     * 示例: satisfy(index &gt;= 0, "index &gt;= 0");
     * </p>
     *
     * @param state
     *         待检测状态。
     * @param expression
     *         待检测表达式。
     */
    public static void satisfy(
            final boolean state,
            final String expression
    ) {
        if (!state) {
            if (expression == null) {
                throw new IllegalStateException("condition not satisfied");
            } else {
                throw new IllegalStateException("condition \"" + expression + "\" not satisfied");
            }
        }
    }
}
